#include "headfiles.h"

entry_t **hashtable_create(int size) {
    if (size <= 0) {
        cout << "Invalid hashtable size" << endl;
        return NULL;
    }

    entry_t **table;
    table = (entry_t **)malloc(sizeof(entry_t *) * size);
    memset(table, 0, sizeof(entry_t *));

    return table;
}

// calculate the hash value of the key
int hashtable_function(char *key, int table_size) {
    if (key == NULL) {
        return -1;
    }

    size_t key_size = strlen(key);
    size_t sum = 0;
    size_t i;
    for (i = 0; i < key_size; i++) {
        sum += key[i];
    }

    return sum % table_size;
}

static void add_entry(entry_t * &root, entry_t *entry) {
    if (root == NULL) {
        root = entry;
    }
    else {
        entry->next = root->next;
        root = entry;
    }

    return;
}

static entry_t *get_entry(entry_t *root, char *key) {
    entry_t *entry = root;
    while (entry) {
        if (strcmp(entry->key, key) == 0) {
            return entry;
        }
        entry = entry->next;
    }

    return NULL;
}

bool hashtable_insert(entry_t **table, 
                      size_t table_size, 
                      char *key, 
                      bool val) {
    if (table == NULL ||
        key == NULL) {
        return false;
    }

    int hash_index = hashtable_function(key, table_size);
    entry_t *existing_entry = get_entry(table[hash_index], key);
    if (existing_entry) {
        existing_entry->val = val;
    }
    else {
        entry_t *new_entry = (entry_t *)malloc(sizeof(entry_t));
        memset(new_entry, 0, sizeof(entry_t));

        new_entry->key = strdup(key);
        new_entry->val = val;

        add_entry(table[hash_index], new_entry);
    }

    return true;
}

bool hashtable_getvalue(entry_t **table, 
                        size_t table_size,
                        char *key, 
                        bool &val) {
    if (table == NULL ||
        key == NULL) {
        return false;
    }

    int hash_index = hashtable_function(key, table_size);
    entry_t *entry = get_entry(table[hash_index], key);
    if (entry == NULL) {
        return false;
    }
    else {
        val = entry->val;
    }

    return true;
}

static bool remove_item(entry_t *&entry, char *key) {
    if (entry == NULL ||
        key == NULL) {
        return false;
    }

    entry_t *e1, *e2;
    if (strcmp(entry->key, key) == 0) {
        e1 = entry;
        entry = entry->next;
        free(e1);
        return true;
    }

    e1 = entry;
    e2 = e1->next;
    while (e2) {
        if (strcmp(e2->key, key) == 0) {
            break;
        }
        e1 = e2;
        e2 = e2->next;
    }

    if (e2) {
        e1->next = e2->next;
        free(e2);
        return true;
    }

    return false;
}

static void remove_entry(entry_t *&entry) {
    entry_t *item, *cur_item;
    item = entry;
    while (item) {
        cur_item = item;
        item = item->next;
        free(cur_item);
    }

    entry = NULL;

    return;
}

bool hashtable_remove(entry_t **table, size_t table_size, char *key) {
    if (table == NULL ||
        key == NULL) {
        return false;
    }
    
    int hash_index = hashtable_function(key, table_size);
    return remove_item(table[hash_index], key);
}

bool hashtable_destroy(entry_t **table, size_t table_size) {
    if (table == NULL) {
        return false;
    }

    size_t i;
    for (i = 0; i < table_size; i++) {
        remove_entry(table[i]);
    }
    free(table);

    return true;
}


